package ngat.oss.simulation;

import java.util.Date;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;

import ngat.phase2.Group;

/** Represents an environmental scenario (seeing profile time series). Each time window between start and end
 * limits of scenario are assigned constant environmental conditions. Times beyond these limits have
 * randomly calculated conditions based on supplied probability ditributions.
 */
public class EnvironmentalScenario implements EnvironmentPredictor {

    /** The name of the scenario.*/
    String scenarioName;

    /** Holds the scenario profile.*/
    private SortedSet seeingProfile;

    /** Probability of EX seeing.*/
    double pex;

    /** Probability of AV seeing.*/
    double pav;

    /** Probability of POOR seeing.*/
    double ppo;

    /** Probability of photometric extinction.*/
    double fpho;
    
    long startTime;
    
    long endTime;

    /** Create an EnvironmentalScenario.*/
    public EnvironmentalScenario() {
	seeingProfile = new TreeSet();
    }
    
    /** Create an EnvironmentalScenario with supplied ID.
     * @param scenarioName The Name/ID of this scenario.
     * @param pex Probability of EX seeing
     * @param pav Probability of AV seeing.
     * @param ppo Probability of POOR seeing.
     * @param fpho Probability of photometric extinction.
     */
    public EnvironmentalScenario(String scenarioName, double pex, double pav, double ppo, double fpho) {
	this();
	this.scenarioName = scenarioName;
	this.pex  = pex;
	this.pav  = pav;
	this.ppo  = ppo;
	this.fpho = fpho;		
    }

    public void setScenarioName(String name)        { this.scenarioName = name;}
    public String getScenarioName() { return scenarioName;}

    public void setProbabilityExcellent(double pex) { this.pex  = pex;}
    public void setProbabilityAverage(double pav)   { this.pav  = pav;}
    public void setProbabilityPoor(double ppo)      { this.ppo  = ppo;}
    public void setProbabilityPhotom(double fpho)   { this.fpho = fpho;}

    public void setStartTime(long t) { this.startTime = t;}
    public void setEndTime(long t)   { this.endTime = t;}

    /** Add an entry to the scenario.*/
    public void addScenarioEntry(SeeingProfileEntry entry) {
	seeingProfile.add(entry);
    }

    /** Returns a list of scenario entries (the seeing profile).*/
    public Iterator listScenarioEntries() {
	return seeingProfile.iterator();
    }

    /** EnvironmentalPredictor implementation.*/
    public EnvironmentSnapshot predictEnvironment(long time) {
	EnvironmentSnapshot env = new EnvironmentSnapshot();
	
	// incase earlier or later then pre-computed period.
	int initsee = randomSeeing();
	env.seeing = initsee;

	// the weather should have been pre-populated so we can just read it out from here.
	if (time < startTime) {	   
	    System.err.println("At "+ScheduleSimulator.sdf.format(new Date(time))+" is Before start");	
	    return env;
	}

	if (time > endTime) {
	    System.err.println("At "+ScheduleSimulator.sdf.format(new Date(time))+" is After end");	 
	    return env;
	}

	Iterator it = seeingProfile.iterator();
	while (it.hasNext()) {
	    SeeingProfileEntry spe = (SeeingProfileEntry)it.next();
	    
	    long            st = spe.stime;
	    long            et = spe.etime;
	    EnvironmentSnapshot envr = spe.env;
	 
	    if (time >= st && time < et) 
		return envr;
	
	}
	
	// outside scenario limits, defaults to random seeing
	return env;
    }

    /** Randomly assigned seeing.*/
    public int randomSeeing() {
	double rnd = Math.random();
	if (rnd < pex)
	    return Group.EXCELLENT;
	else if
	    (rnd < pex + pav)
	    return Group.AVERAGE;
	else if
	    (rnd < pex + pav + ppo)
	    return Group.POOR;
	else
	    return Group.CRAP;
    }
    
    /** Randomly assigned photometrics.*/
    public boolean randomPhotom() {
	double rnd = Math.random();
	if (rnd < fpho)
	    return true;
	return false;
    }

    /** Holds entries for the seeing profile time-series.*/
    public static class SeeingProfileEntry implements Comparable {

	 /** Start of window.*/
	public long stime;

	 /** End of window.*/
	public long etime;

	 /** Window snapshot.*/
	public EnvironmentSnapshot env;

	 /** Create a SeeingProfileEntry (a window of constant environmental conditions).
	  * @param stime Start of window.
	  * @param etime End of window.
	  * @param env EnvironmentSnapshot during window.
	  */
	 public SeeingProfileEntry(long stime, long etime, EnvironmentSnapshot env) {
	     this.stime = stime;
	     this.etime = etime;
	     this.env   = env;
	 }

	 /** Comparator (uses only stime).*/
	public int compareTo(Object o) {
	 
	    SeeingProfileEntry s = (SeeingProfileEntry)o;
	 
	    if (stime < s.stime)
		return -1;
	    if (stime > s.stime)
		return 1;
	    return 0;

	}

    }


}
